package pl.darbat.photogallery.list;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

import pl.darbat.photogallery.Debug;
import pl.darbat.photogallery.R;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.Environment;
import android.util.Log;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.ImageView;

public class ImageDownload implements DownloadAction<String, Bitmap> {

	private static final String TAG = "ImageDownload";
	private Context mContext;
	private int mAnimationRes;
	private Drawable mLoading;
	private Animation mRotateAnimation;
	private File mCacheDir;
	
	//private Drawable mLoadingIndetermined;
	//private Interpolator mLoadingInterpolator;
	//private Animation mLoadingAnimation;
	//private AnimationDrawable mLoadingAnimated;
	
	
	public ImageDownload(Context context, int animationRes){
		if(Debug.IMAGEDOWNLOAD) Log.v(TAG, "ImageDownload");
		mContext = context.getApplicationContext();
		mAnimationRes = animationRes;
		
		//Option 2:
		mLoading = context.getResources().getDrawable(R.drawable.spinner_01);
		mRotateAnimation = AnimationUtils.loadAnimation(mContext,R.anim.rotate);
		mRotateAnimation.start();
		
		//Option 4:
		//ProgressBar pb = new ProgressBar(mContext);
		//pb.setIndeterminate(true);
		//mLoadingIndetermined = pb.getIndeterminateDrawable();
		//mLoadingInterpolator = pb.getInterpolator();
		//mLoadingAnimation = AnimationUtils.loadAnimation(mContext,R.anim.rotate);
		//mLoadingAnimation.setInterpolator(mLoadingInterpolator);
		//mLoadingAnimation.start();
		
		
	    //Find the dir to save cached images
        if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
            mCacheDir = new File(android.os.Environment.getExternalStorageDirectory(),".PhotoGallery");
        } else {
            mCacheDir = context.getCacheDir();
        }
        if(!mCacheDir.exists()) {
            mCacheDir.mkdirs();
        }
        if(Debug.IMAGEDOWNLOAD) Log.v(TAG, "Cache path: " + mCacheDir.toURI().toString());

	}
	
	public void clearCache(){
        File[] files=mCacheDir.listFiles();
        for(File f:files)
            f.delete();
	}
	
	public void cancelDownload() {

		
	}
	
	/*
	 * Working with scaling
	 */
	public Bitmap download(String link, int imageSize) {
		try{

			URL url = new URL(link);
			String filename = url.getFile().replace("/", ".");
			if(Debug.IMAGEDOWNLOAD) Log.v(TAG, "filename: " + filename);
	        //String filename = "." + String.valueOf(link.hashCode());
	        Bitmap cachedBitmap = null;
	        File storedFile = new File(mCacheDir, filename);
	        if(storedFile.exists()){
	        	InputStream in = new FileInputStream(storedFile);
	        	byte[] bytes = new byte[(int) storedFile.length()];
	        	in.read(bytes);
	        	in.close();
	        	cachedBitmap = decodeByteArray(bytes,imageSize);
	        }
			if (cachedBitmap != null){
				if(Debug.IMAGEDOWNLOAD) Log.v(TAG, "image found in cache");
				return cachedBitmap;
			} else {
				if(Debug.IMAGEDOWNLOAD) Log.v(TAG, "image not found in cache");
				//URL url = new URL(link);
				HttpURLConnection connection = (HttpURLConnection) url.openConnection();

				// determine the image size and allocate a buffer
				int fileSize = connection.getContentLength();
				if(fileSize <= 0){
					connection.disconnect();
					return null;
				}
				byte[] imageData = new byte[fileSize];
				
				// download the file
				if(Debug.IMAGEDOWNLOAD) Log.v(TAG, "fetching image " + link + " (" + fileSize + ")");
				BufferedInputStream istream = new BufferedInputStream(connection.getInputStream());
				int bytesRead = 0;
				int offset = 0;
				while (bytesRead != -1 && offset < fileSize) {
					bytesRead = istream.read(imageData, offset, fileSize - offset);
					offset += bytesRead;
				}

				// clean up
				istream.close();
				connection.disconnect();
				
				//store compressed Bitmap
				
				try {
					FileOutputStream out = new FileOutputStream(storedFile);
					out.write(imageData); 
					out.close();
				} catch(Exception e) {
					if(Debug.IMAGEDOWNLOAD) Log.e(TAG, "FileOutputStream", e);
				}
				return decodeByteArray(imageData,imageSize);
			}
		} catch (Exception e) {
			if(Debug.IMAGEDOWNLOAD) Log.e(TAG,"",e);
		}
		return null;
	
	}
	
//	/*
//	 * Not working w/o scaling
//	 */
//	public Drawable download2(String link, int imageSize) {
//		try {
//			URL url = new URL(link);
//			URLConnection connection = url.openConnection();
//			connection.setUseCaches(true);
//			Object response = connection.getContent();
//			if (response instanceof Bitmap) {
//				return new BitmapDrawable((Bitmap)response);
//			}else{
//				if(Debug.IMAGEDOWNLOAD) Log.w(TAG,"Response is not a Bitmap");
//			}
//		} catch (MalformedURLException e) {
//			if(Debug.IMAGEDOWNLOAD) Log.e(TAG,"",e);
//		} catch (IOException e) {
//			if(Debug.IMAGEDOWNLOAD) Log.e(TAG,"",e);
//		}
//		return null;
//		
//	}
	
//	/*
//	 * Working w/o scaling
//	 */
//	public Drawable download1(String link, int imageSize) {
//		if(Debug.IMAGEDOWNLOAD) Log.v(TAG, "download: " + imageSize);
//		final HttpClient client = new DefaultHttpClient();
//		final HttpGet getRequest = new HttpGet(link);
//
//		try {
//			HttpResponse response = client.execute(getRequest);
//			final int statusCode = response.getStatusLine().getStatusCode();
//			if (statusCode != HttpStatus.SC_OK) {
//				Log.w(TAG, "Error " + statusCode
//						+ " while retrieving bitmap from " + link);
//				return null;
//			}
//			
//			final HttpEntity entity = response.getEntity();
//			if (entity != null) {
//				InputStream inputStream = null;
//				try {
//					inputStream = entity.getContent();
//					// return BitmapFactory.decodeStream(inputStream);
//					// Bug on slow connections, fixed in future release.
//					
//					
//					return new BitmapDrawable(decodeStream(new FlushedInputStream(inputStream), imageSize));
//					//return new BitmapDrawable(BitmapFactory.decodeStream(new FlushedInputStream(inputStream)));
//				} finally {
//					if (inputStream != null) {
//						inputStream.close();
//					}
//					entity.consumeContent();
//				}
//			}
//		} catch (IOException e) {
//			getRequest.abort();
//			Log.w(TAG, "I/O error while retrieving bitmap from " + link, e);
//		} catch (IllegalStateException e) {
//			getRequest.abort();
//			Log.w(TAG, "Incorrect URL: " + link);
//		} catch (Exception e) {
//			getRequest.abort();
//			Log.w(TAG, "Error while retrieving bitmap from " + link, e);
//		}
//		return null;
//
//	}
	


	private Bitmap decodeByteArray(byte[] byteArray, int imageSize) {
		if(Debug.IMAGEDOWNLOAD) Log.v(TAG, "decodeByteArray: " + imageSize);
		BitmapFactory.Options opt = new BitmapFactory.Options();
        opt.inJustDecodeBounds = true;
		BitmapFactory.decodeByteArray(byteArray, 0, byteArray.length,opt);
		int widthTmp = opt.outWidth, heightTmp = opt.outHeight;
		int scale = 1;
		if(imageSize > 0){
			while(true){
				if(widthTmp/2 < imageSize && heightTmp/2 < imageSize) {
					break;
				}
				widthTmp /= 2;
				heightTmp /= 2;
				scale *= 2;
			}
		}
		BitmapFactory.Options opt2 = new BitmapFactory.Options();
		opt2.inSampleSize = scale;
		return BitmapFactory.decodeByteArray(byteArray, 0, byteArray.length,opt2);
	}
	
	private Bitmap decodeStream(InputStream flushedInputStream, int imageSize) {
		if(Debug.IMAGEDOWNLOAD) Log.v(TAG, "decodeStream: " + imageSize);
        BitmapFactory.Options opt = new BitmapFactory.Options();
        opt.inJustDecodeBounds = true;
        BitmapFactory.decodeStream(flushedInputStream, null, opt);
        
        //Find the correct scale value. It should be the power of 2.

        int widthTmp = opt.outWidth, heightTmp = opt.outHeight;
        int scale = 1;
        while(true){
            if(widthTmp/2 < imageSize && heightTmp/2 < imageSize) {
                break;
            }
            widthTmp /= 2;
            heightTmp /= 2;
            scale *= 2;
        }
        BitmapFactory.Options opt2 = new BitmapFactory.Options();
        opt2.inSampleSize = scale;
        return BitmapFactory.decodeStream(flushedInputStream, null, opt2);
		
	}

	public void applyDownloadedResult(Bitmap result, View view, boolean fromCache) {
		if(Debug.IMAGEDOWNLOAD) Log.v(TAG, "applyDownloadedResult");
		if (result != null && view instanceof ImageView) {
			((ImageView) view).setImageBitmap(result);
			if(!fromCache){
				if(mAnimationRes != 0){
					view.clearAnimation();
					//Animation inAnimation = AnimationUtils.loadAnimation(mContext, mAnimationRes);
					//view.startAnimation(inAnimation);
				} else {
					view.clearAnimation();
				}
			} else {
				view.clearAnimation();
			}
		}
	}
	
	public void setLoading(View view) {
		if(Debug.IMAGEDOWNLOAD) Log.v(TAG, "setLoading");
		if (view != null && view instanceof ImageView) {
			
			
			ImageView imageview = (ImageView) view;
			//Option 1:
			//imageview.setImageResource(R.drawable.pl_foto_com);
			//Option 2:
			imageview.setImageDrawable(mLoading);
			imageview.setAnimation(mRotateAnimation);
			//Option 3:
			//imageview.setImageResource(R.drawable.progress);
			//((AnimationDrawable) imageview.getDrawable()).start();
			//Option 4:
			//imageview.setImageDrawable(mLoadingIndetermined);
			//imageview.setAnimation(mLoadingAnimation);
			
			//if (mLoadingIndetermined instanceof AnimationDrawable) {
			//	if(Debug.IMAGEDOWNLOAD) Log.v(TAG,"Starting loading animation...");
			//	((AnimationDrawable) mLoadingIndetermined).start();
			//}

		}
	}

	/*
	 * An InputStream that skips the exact number of bytes provided, unless it
	 * reaches EOF.
	 */
	static class FlushedInputStream extends FilterInputStream {
		public FlushedInputStream(InputStream inputStream) {
			super(inputStream);
		}

		@Override
		public long skip(long n) throws IOException {
			long totalBytesSkipped = 0L;
			while (totalBytesSkipped < n) {
				long bytesSkipped = in.skip(n - totalBytesSkipped);
				if (bytesSkipped == 0L) {
					int b = read();
					if (b < 0) {
						break; // we reached EOF
					} else {
						bytesSkipped = 1; // we read one byte
					}
				}
				totalBytesSkipped += bytesSkipped;
			}
			return totalBytesSkipped;
		}
	}




}
